package dict

// Package dict 提供了一个基于 sync.Map 的并发安全字典

import (
	"crypto/rand"
	"sync"
	"sync/atomic"
)

// Dict 是一个并发安全的字典
type Dict[K comparable, V any] struct {
	syncMap sync.Map
	length  int64
}

// NewDict 创建一个新的 Dict 实例
func NewDict[K comparable, V any]() *Dict[K, V] {
	return &Dict[K, V]{
		syncMap: sync.Map{}, // 初始化 sync.Map
		length:  0,          // 初始化长度计数
	}
}

// Get 获取指定键的值
func (d *Dict[K, V]) Get(key K) (V, bool) {
	value, ok := d.syncMap.Load(key)
	if !ok {
		var zero V
		return zero, false
	}
	return value.(V), true
}

// GetRandomItem 返回字典中的一个随机键值对，使用加密级别的随机数生成器
func (d *Dict[K, V]) GetRandomItem() (K, V, bool) {
	n := d.Len()
	if n == 0 {
		var zeroK K
		var zeroV V
		return zeroK, zeroV, false
	}

	// 使用 crypto/rand.Read 生成随机字节
	var randomBytes [4]byte // 假设索引是 int，最多占用 4 字节
	_, err := rand.Read(randomBytes[:])
	if err != nil {
		// 如果生成失败，可以处理错误或回退到 math/rand
		var zeroK K
		var zeroV V
		return zeroK, zeroV, false
	}

	// 将随机字节转换为整数
	index := int(randomBytes[0]) // 示例：仅使用第一个字节
	// 或者将其转换为更大的整数，例如：
	// index := int(binary.LittleEndian.Uint32(randomBytes[:]))

	// 确保索引在有效范围内
	index = index % n

	var key K
	var value V
	var count int

	// 遍历字典，找到随机索引对应的键值对
	d.syncMap.Range(func(k, v interface{}) bool {
		if count == index {
			key = k.(K)
			value = v.(V)
			return false // 停止遍历
		}
		count++
		return true
	})

	return key, value, true
}

// Set 存储键值对，并原子更新长度计数
func (d *Dict[K, V]) Set(key K, value V) {
	_, loaded := d.syncMap.LoadOrStore(key, value)
	if !loaded {
		atomic.AddInt64(&d.length, 1)
	}
}

// Len 返回字典的长度
func (d *Dict[K, V]) Len() int {
	return int(atomic.LoadInt64(&d.length)) // 原子读取长度计数
}

// Keys 返回所有键的切片
func (d *Dict[K, V]) Keys() []K {
	var keys []K

	// 使用 Range 遍历 sync.Map
	d.syncMap.Range(func(key, value interface{}) bool {
		keys = append(keys, key.(K))
		return true
	})

	return keys
}

// Values 返回所有值的切片
func (d *Dict[K, V]) Values() []V {
	var values []V

	// 使用 Range 遍历 sync.Map
	d.syncMap.Range(func(key, value interface{}) bool {
		values = append(values, value.(V))
		return true
	})

	return values
}

// Items 返回所有键值对的切片，每个元素是一个包含键和值的结构体
func (d *Dict[K, V]) Items() []struct {
	Key   K
	Value V
} {
	var items []struct {
		Key   K
		Value V
	}

	// 使用 Range 遍历 sync.Map
	d.syncMap.Range(func(key, value interface{}) bool {
		items = append(items, struct {
			Key   K
			Value V
		}{Key: key.(K), Value: value.(V)})
		return true
	})

	return items
}

// SetDefault 设置键值对，如果键不存在，则存储新值并返回新值；如果键存在，则返回旧值
func (d *Dict[K, V]) SetDefault(key K, value V) (V, bool) {
	actual, loaded := d.syncMap.LoadOrStore(key, value)
	if !loaded { // 如果键不存在，则存储新值并返回新值
		atomic.AddInt64(&d.length, 1)
	}
	return actual.(V), loaded
}

// Delete 删除指定键，并原子更新长度计数
func (d *Dict[K, V]) Delete(key K) bool {
	_, loaded := d.syncMap.LoadAndDelete(key) // 从 syncMap 中加载与 key 关联的值，并返回一个布尔值 loaded	 如果 key 存在于 syncMap 中，loaded 为 true，否则为 false
	if loaded {
		atomic.AddInt64(&d.length, -1)
	}
	return loaded
}

// Pop 删除并返回指定键的值
func (d *Dict[K, V]) Pop(key K) (V, bool) {
	var zero V
	value, loaded := d.syncMap.LoadAndDelete(key)
	if loaded {
		atomic.AddInt64(&d.length, -1)
		return value.(V), loaded
	}
	return zero, loaded
}

// PopRandomItem 返回并删除字典中的一个随机键值对
func (d *Dict[K, V]) PopRandomItem() (K, V, bool) {
	var key K
	var value V
	var found bool

	// 使用 Range 遍历 sync.Map
	d.syncMap.Range(func(k, v interface{}) bool {
		key = k.(K)
		value = v.(V)
		found = true
		return false // 停止遍历
	})

	if found {
		// 删除随机选择的键值对
		d.Delete(key)
		return key, value, true
	}

	// 如果字典为空，返回 false
	return key, value, false
}

// Clear 清空字典，并原子重置长度计数
func (d *Dict[K, V]) Clear() {
	atomic.StoreInt64(&d.length, 0) // 原子重置长度计数
	d.syncMap.Clear()               // 清空 sync.Map
}

// Exists 检查字典中是否存在指定的键
func (d *Dict[K, V]) Exists(key K) bool {
	_, ok := d.syncMap.Load(key) //  从 syncMap 中加载与 key 关联的值，并返回一个布尔值 ok	 如果 key 存在于 syncMap 中，ok 为 true，否则为 false
	return ok                    //  返回布尔值 ok，表示 key 是否存在于 syncMap 中
}

// Update 批量更新多个键值对
func (d *Dict[K, V]) Update(kv map[K]V) {
	// 遍历传入的键值对映射
	for key, value := range kv {
		// 调用 Set 方法更新字典中的键值对
		d.Set(key, value)
	}
}

// FromMap 从一个普通的 map 创建 Dict 实例
func FromMap[K comparable, V any](m map[K]V) *Dict[K, V] {
	d := NewDict[K, V]()
	for key, value := range m {
		d.Set(key, value)
	}
	return d
}
